package com.unlp.javaclickers.docente.vistas;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.List;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import org.apache.log4j.Logger;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.unlp.javaclickers.docente.logic.ServicioProblema;
import com.unlp.javaclickers.docente.logic.impl.ServicioProblemaImpl;
import com.unlp.javaclickers.modelo.Clase;
import com.unlp.javaclickers.modelo.Problema;
import com.unlp.javaclickers.modelo.Respuesta;

public class AtenderClientes implements Runnable {
	private JFrame frame;
	private EsperandoAlumnos esperandoAlumnos;
	private Clase clase;
	private int cantfin = 0;
	private ServerSocket server = null;
	private Thread hiloServidor;
	private List<Thread> hilos = Lists.newArrayList();
	private boolean aceptarAlumnos = true;
	private PreguntaEnCurso preguntaEnCurso = new PreguntaEnCurso();
	private List<TareaSocket> t = Lists.newArrayList();
	private Map<String, Integer> grafico;
	private List<Map<String, Integer>> resultados= Lists.newArrayList();
	private List<Problema> problemas=Lists.newArrayList();
	private int cantAlumnos;
	private Problema problemaActual;
	private boolean sigo;
	private ServicioProblema servicioProblema;
	private int cantCorrectas=0;

	private static final Logger LOGGER = Logger
			.getLogger(AtenderClientes.class);

	public AtenderClientes(JFrame frame2, EsperandoAlumnos esperandoAl,
			Clase c, int cant) {
		esperandoAlumnos = esperandoAl;
		frame = frame2;
		clase = c;
		cantAlumnos = cant;
		this.servicioProblema = new ServicioProblemaImpl();
	}

	@Override
	public void run() {
		try {
			server = new ServerSocket(9900);
			cantAlumnos = 0;
			while (this.getAceptarAlumnos()) {
				try {
					Socket socket = server.accept();
					cantAlumnos++;
					esperandoAlumnos.actualizarCantAlumnos(cantAlumnos);
					TareaSocket ts = new TareaSocket(socket, clase, this);
					Thread th = new Thread(ts);
					hilos.add(th);
					t.add(ts);
				} catch (Exception e) {
					if (cantAlumnos == 0) {
						JOptionPane
								.showMessageDialog(esperandoAlumnos.getFrame(),
										"No se conecto ningun alumno, vuelva a intentarlo");
						esperandoAlumnos.getFrame().setVisible(false);
						frame.setVisible(true);
					} else {
						JOptionPane.showMessageDialog(
								esperandoAlumnos.getFrame(),
								"No se recibiran mas alumnos");
						esperandoAlumnos.getFrame().setVisible(false);
						preguntaEnCurso.getFrame().setVisible(true);
						this.problemaActual = this.servicioProblema
								.getFirstProblem(this.clase.getProblemas());
						this.sigo = this.problemaActual != null;
						for (Thread h : hilos) {
							h.start();
						}
						while (sigo) {
							problemas.add(problemaActual);
							preguntaEnCurso.cambiarNombreTiempoProblema(
									problemaActual.getNombre(),
									problemaActual.getTiempo());
							List<Respuesta> respuestas = problemaActual
									.getRespuestas();
							grafico = Maps.newHashMap();
							for (Respuesta r : respuestas) {
								grafico.put(r.getNombre(), 0);
							}
							LOGGER.info(String.format("Grafico en 0: %s",
									grafico)); // debug
							synchronized (this) {
								// t.wait((Integer) problema.get("tiempo"));
								this.wait();
							}
							LOGGER.info(String.format("Grafico completo: %s",
									grafico)); // debug
							MostrarChartProfesor window = new MostrarChartProfesor(
									AtenderClientes.this.preguntaEnCurso
											.getFrame(),
									AtenderClientes.this.grafico,
									AtenderClientes.this,
									problemaActual);
							resultados.add(grafico);
							AtenderClientes.this.preguntaEnCurso.getFrame()
									.setVisible(false);
							window.getFrame().setVisible(true);
							synchronized (this) {
								this.wait();
							}
							preguntaEnCurso.cancelarTimer(); // PROBAAR
							LOGGER.info(String
									.format("Se despierta a cada thread"));
							problemaActual = this.servicioProblema
									.getNextProblem(problemaActual,
											cantCorrectas,
											this.getTotalAlumnos());
							sigo = problemaActual != null;
							cantCorrectas = 0;
							for (TareaSocket thread : t) {
								synchronized (thread) {
									thread.notify();
								}
							}
						}
						preguntaEnCurso.getFrame().setVisible(false);
						ResumenDeClase resumenDeClase= new ResumenDeClase(frame, resultados,problemas, clase);
						resumenDeClase.getFrame().setVisible(true);
					}
				}
			}

		} catch (SocketTimeoutException s) {
			LOGGER.error("Socket timed out!", s);
			JOptionPane
					.showMessageDialog(preguntaEnCurso.getFrame(),
							"Ocurrio un error en la conexion de los alumnos, Vuelva a intentarlo");
			preguntaEnCurso.getFrame().setVisible(false);
			frame.setVisible(true);
		} catch (IOException e) {
			LOGGER.error("Error al iniciar el server", e);
			JOptionPane.showMessageDialog(preguntaEnCurso.getFrame(),
					"Ocurrio un error en la conexion , Vuelva a intentarlo");
			preguntaEnCurso.getFrame().setVisible(false);
			frame.setVisible(true);
		} catch (InterruptedException e) {
			LOGGER.error(e);
			JOptionPane.showMessageDialog(preguntaEnCurso.getFrame(),
					"Ocurrio un error en la conexion , Vuelva a intentarlo");
			preguntaEnCurso.getFrame().setVisible(false);
			frame.setVisible(true);
		}

	}

	public synchronized JFrame getFrame() {
		return frame;
	}

	public synchronized void setFrame(JFrame frame) {
		this.frame = frame;
	}

	public synchronized boolean getAceptarAlumnos() {
		return aceptarAlumnos;
	}

	public synchronized void setAceptarAlumnos(boolean aceptarAlumnos) {
		this.aceptarAlumnos = aceptarAlumnos;
		try {
			server.close();
		} catch (IOException e) {
			LOGGER.error("Error trying to set aceptarAlumnos", e);
		}
	}

	public synchronized void actualizarGrafico(String elegida) {
		grafico.put(elegida, grafico.get(elegida) + 1);
		if (elegida.equals(problemaActual.getCorrecta().getNombre())) {
			cantCorrectas++;
		}
		LOGGER.info(String.format("incremento elegida: %s", elegida));// debug
		cantfin++;
		if (cantfin == cantAlumnos) {
			cantfin = 0;
			this.notify();
			LOGGER.info(String.format("llego el ulitmo")); // debug
		}

	}

	public synchronized void errorConSocket(TareaSocket tareaSocket) {
		t.remove(tareaSocket);
		cantAlumnos--;
		JOptionPane
				.showMessageDialog(
						preguntaEnCurso.getFrame(),
						"Ocurrio un error en la conexion de uno de los alumnos, se quitara de la lista de alumnos");
		if (t.isEmpty()) {
			preguntaEnCurso.getFrame().setVisible(false);
			frame.setVisible(true);
		}

	}

	public Thread getHiloServidor() {
		return hiloServidor;
	}

	public void setHiloServidor(Thread hiloServidor) {
		this.hiloServidor = hiloServidor;
	}

	public PreguntaEnCurso getPreguntaEnCurso() {
		return preguntaEnCurso;
	}

	public void setPreguntaEnCurso(PreguntaEnCurso preguntaEnCurso) {
		this.preguntaEnCurso = preguntaEnCurso;
	}

	public int getTotalAlumnos() {
		return this.cantAlumnos;
	}

	public Problema getProblemaActual() {
		return problemaActual;
	}

	public void setProblemaActual(Problema problemaActual) {
		this.problemaActual = problemaActual;
	}

	public boolean isSigo() {
		return sigo;
	}

	public void setSigo(boolean sigo) {
		this.sigo = sigo;
	}

}
